Skip to content

Conversation

@feeblefakie
Copy link
Contributor

This is an automated backport of the following:

Please merge this PR after all checks have passed.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @feeblefakie, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request backports the Cassandra permission test functionality to the branch(3.15). It introduces new integration tests to verify Cassandra permissions for both normal and admin users. The build configuration is updated to manage the inclusion and exclusion of specific test files. Additionally, the Cassandra environment is enhanced to support retrieving properties for normal users.

Highlights

  • Backport: This PR backports the Cassandra permission test functionality to the branch(3.15).
  • Integration Tests: Adds new integration tests for Cassandra permissions, including tests for both normal users and admin users.
  • Build Configuration: Updates the build.gradle file to include and exclude specific Cassandra permission test files in different test tasks.
  • Cassandra Environment: Introduces a method to retrieve properties for normal users, including username and password.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The code changes introduce Cassandra permission tests. Please address the potential CQL injection vulnerabilities in CassandraPermissionTestUtils.java and resource leaks in the waitFor... methods in the test classes.

Comment on lines +20 to +26
public void createNormalUser(String userName, String password) {
clusterManager
.getSession()
.execute(
String.format(
"CREATE ROLE %s WITH PASSWORD = '%s' AND LOGIN = true", userName, password));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This method uses String.format to construct a CQL query, which is vulnerable to CQL injection. Both userName and password are directly embedded into the query string. A malicious username or password could alter the intended query.

Since the Cassandra driver does not support bind variables for DDL statements, you should sanitize the inputs. For the username, use com.datastax.driver.core.Metadata.quoteIfNecessary() to properly quote it. For the password, you need to escape single quotes by doubling them (e.g., password.replace("'", "''")).

The same vulnerability exists in dropNormalUser() and grantRequiredPermission() for the userName parameter.

Suggested change
public void createNormalUser(String userName, String password) {
clusterManager
.getSession()
.execute(
String.format(
"CREATE ROLE %s WITH PASSWORD = '%s' AND LOGIN = true", userName, password));
}
public void createNormalUser(String userName, String password) {
clusterManager
.getSession()
.execute(
String.format(
"CREATE ROLE %s WITH PASSWORD = '%s' AND LOGIN = true",
com.datastax.driver.core.Metadata.quoteIfNecessary(userName),
password.replace("'", "''")));
}

Comment on lines +29 to +31
public void dropNormalUser(String userName) {
clusterManager.getSession().execute(String.format("DROP ROLE %s", userName));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This method is vulnerable to CQL injection. The userName is not sanitized before being used in the query. Use com.datastax.driver.core.Metadata.quoteIfNecessary() to properly quote the username.

  public void dropNormalUser(String userName) {
    clusterManager
        .getSession()
        .execute(
            String.format(
                "DROP ROLE %s", com.datastax.driver.core.Metadata.quoteIfNecessary(userName)));
  }

Comment on lines +41 to +49
private String[] getGrantPermissionStatements(String userName) {
return new String[] {
String.format("GRANT CREATE ON ALL KEYSPACES TO %s", userName),
String.format("GRANT DROP ON ALL KEYSPACES TO %s", userName),
String.format("GRANT ALTER ON ALL KEYSPACES TO %s", userName),
String.format("GRANT SELECT ON ALL KEYSPACES TO %s", userName),
String.format("GRANT MODIFY ON ALL KEYSPACES TO %s", userName)
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This method is vulnerable to CQL injection. The userName is not sanitized before being used in the query. Use com.datastax.driver.core.Metadata.quoteIfNecessary() to properly quote the username.

  private String[] getGrantPermissionStatements(String userName) {
    String quotedUserName = com.datastax.driver.core.Metadata.quoteIfNecessary(userName);
    return new String[] {
      String.format("GRANT CREATE ON ALL KEYSPACES TO %s", quotedUserName),
      String.format("GRANT DROP ON ALL KEYSPACES TO %s", quotedUserName),
      String.format("GRANT ALTER ON ALL KEYSPACES TO %s", quotedUserName),
      String.format("GRANT SELECT ON ALL KEYSPACES TO %s", quotedUserName),
      String.format("GRANT MODIFY ON ALL KEYSPACES TO %s", quotedUserName)
    };
  }

Comment on lines +44 to +62
protected void waitForNamespaceCreation() {
try {
AdminTestUtils utils = getAdminTestUtils(TEST_NAME);
int retryCount = 0;
while (retryCount < MAX_RETRY_COUNT) {
if (utils.namespaceExists(NAMESPACE)) {
utils.close();
return;
}
Uninterruptibles.sleepUninterruptibly(
SLEEP_BETWEEN_RETRIES_SECONDS, java.util.concurrent.TimeUnit.SECONDS);
retryCount++;
}
utils.close();
throw new RuntimeException("Namespace was not created after " + MAX_RETRY_COUNT + " retries");
} catch (Exception e) {
throw new RuntimeException("Failed to wait for namespace creation", e);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This method, and the other waitFor... methods in this file, have a potential resource leak and contain duplicated logic. The AdminTestUtils instance is not closed if an exception is thrown during an operation like utils.namespaceExists(). Using a try-finally block is crucial to guarantee that resources are always released.

Additionally, all four waitFor... methods (waitForNamespaceCreation, waitForTableCreation, waitForNamespaceDeletion, waitForTableDeletion) are nearly identical. This duplication makes the code harder to maintain. I recommend refactoring them into a single private helper method.

  protected void waitForNamespaceCreation() {
    AdminTestUtils utils = getAdminTestUtils(TEST_NAME);
    try {
      for (int i = 0; i < MAX_RETRY_COUNT; i++) {
        if (utils.namespaceExists(NAMESPACE)) {
          return;
        }
        Uninterruptibles.sleepUninterruptibly(
            SLEEP_BETWEEN_RETRIES_SECONDS, java.util.concurrent.TimeUnit.SECONDS);
      }
      throw new RuntimeException("Namespace was not created after " + MAX_RETRY_COUNT + " retries");
    } catch (Exception e) {
      throw new RuntimeException("Failed to wait for namespace creation", e);
    } finally {
      utils.close();
    }
  }

Comment on lines +43 to +60
protected void waitForTableCreation() {
try {
AdminTestUtils utils = getAdminTestUtils(TEST_NAME);
int retryCount = 0;
while (retryCount < MAX_RETRY_COUNT) {
if (utils.tableExists(NAMESPACE, TABLE)) {
utils.close();
return;
}
Uninterruptibles.sleepUninterruptibly(SLEEP_BETWEEN_RETRIES_SECONDS, TimeUnit.SECONDS);
retryCount++;
}
utils.close();
throw new RuntimeException("Table was not created after " + MAX_RETRY_COUNT + " retries");
} catch (Exception e) {
throw new RuntimeException("Failed to wait for table creation", e);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This method has a potential resource leak. The AdminTestUtils instance utils is not closed if an exception is thrown from utils.tableExists(). It's better to use a try-finally block to ensure utils.close() is always called.

  protected void waitForTableCreation() {
    AdminTestUtils utils = getAdminTestUtils(TEST_NAME);
    try {
      for (int i = 0; i < MAX_RETRY_COUNT; i++) {
        if (utils.tableExists(NAMESPACE, TABLE)) {
          return;
        }
        Uninterruptibles.sleepUninterruptibly(SLEEP_BETWEEN_RETRIES_SECONDS, TimeUnit.SECONDS);
      }
      throw new RuntimeException("Table was not created after " + MAX_RETRY_COUNT + " retries");
    } catch (Exception e) {
      throw new RuntimeException("Failed to wait for table creation", e);
    } finally {
      utils.close();
    }
  }

@brfrn169 brfrn169 merged commit 2990a0c into 3.15 Jul 10, 2025
51 checks passed
@brfrn169 brfrn169 deleted the 3.15-pull-2822 branch July 10, 2025 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants